// controllers/uploadController.js

const fs = require('fs');                    // 同步 fs（用于 existsSync, mkdirSync）
const fsPromises = require('fs').promises;   // 异步 Promise 版本（用于 copyFile, stat 等）
const path = require('path');
const connection = require('../../config/mysql.js');
const { addUserLog } = require("./log.js");

/**
 * 工具函数：构建完整路径
 * 根据 parent_id 查询父路径，拼接新路径
 * @param {number|null} parentId - 父文件夹 ID，null 表示根目录
 * @returns {Promise<string>} 完整的虚拟路径（如：/images 或 /documents/work）
 * @throws {Error} 当父文件夹不存在时抛出错误
 */
const buildFolderPath = async (parentId) => {
    if (!parentId) return '/';

    const [rows] = await connection.query(
        'SELECT parent_id, path, name FROM folders WHERE id = ? AND status = 1',
        [parentId]
    );

    if (rows.length === 0) {
        throw new Error('父文件夹不存在');
    }

    const parent = rows[0];
    return parent.path + (parent.path === '/' ? '' : '/') + encodeURIComponent(parent.name);
};

// ========================================================================
//                            文件夹操作
// ========================================================================

/**
 * 创建新文件夹
 * @route POST /api/folder/create
 * @access 私有（需登录）
 */
exports.createFolder = async (ctx) => {
    const { folderName, parentId } = ctx.request.body;
    const userId = ctx.tokenObj?.id;

    if (!folderName || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '缺少必填字段：folderName 或 用户未登录' };
        return;
    }

    const parentFolderId = parentId || null;
    let folderPath;

    try {
        // 构建路径
        if (parentFolderId) {
            const parentPath = await buildFolderPath(parentFolderId);
            folderPath = `${parentPath}/${encodeURIComponent(folderName)}`;
        } else {
            folderPath = `/${encodeURIComponent(folderName)}`;
        }

        // 检查重名
        const [existing] = await connection.query(
            'SELECT id FROM folders WHERE parent_id = ? AND name = ? AND status = 1',
            [parentFolderId, folderName]
        );
        if (existing.length > 0) {
            ctx.status = 400;
            ctx.body = { code: 400, msg: '同级目录下已存在相同名称的文件夹' };
            return;
        }

        // 插入数据库
        const [result] = await connection.query(
            `INSERT INTO folders (name, parent_id, path, creationUser, status) VALUES (?, ?, ?, ?, 1)`,
            [folderName, parentFolderId, folderPath, userId]
        );

        const folderId = result.insertId;

        await addUserLog(userId, `创建文件夹 "${folderName}" (ID: ${folderId})`, 11);

        ctx.body = {
            code: 200,
            msg: '文件夹创建成功',
            data: {
                id: folderId,
                name: folderName,
                parent_id: parentFolderId,
                path: folderPath,
                created_at: new Date()
            }
        };
    } catch (error) {
        console.error('创建文件夹失败:', error);
        if (error.message === '父文件夹不存在') {
            ctx.status = 400;
            ctx.body = { code: 400, msg: '父文件夹不存在或已被删除' };
        } else {
            ctx.status = 500;
            ctx.body = { code: 500, msg: '服务器内部错误，创建文件夹失败' };
        }
    }
};

/**
 * 获取指定目录下的文件夹列表
 * @route GET /api/folder/list
 * @access 私有（需登录）
 */
exports.getFolderList = async (ctx) => {
    const parentId = ctx.query.parentId || null;
    const userId = ctx.tokenObj?.id;

    if (!userId) {
        ctx.status = 401;
        ctx.body = { code: 401, msg: '用户未登录' };
        return;
    }

    try {
        const [folders] = await connection.query(
            `SELECT id, name, parent_id, path, created_at, updated_at, text 
             FROM folders 
             WHERE parent_id = ? AND status = 1 
             ORDER BY updated_at DESC`,
            [parentId]
        );

        ctx.body = {
            code: 200,
            msg: '获取文件夹列表成功',
            data: folders
        };
    } catch (error) {
        console.error('获取文件夹列表失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '服务器内部错误' };
    }
};

/**
 * 删除文件夹（逻辑删除 + 级联子项）
 * @route POST /api/folder/delete
 * @access 私有（需登录）
 */
exports.deleteFolder = async (ctx) => {
    const { id } = ctx.request.body;
    const userId = ctx.tokenObj?.id;

    if (!id || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '缺少必填字段：id 或 用户未登录' };
        return;
    }

    /**
     * 辅助函数：递归获取所有子文件夹 ID（包括自身）
     * @param {object} conn - 数据库连接
     * @param {number} folderId - 当前文件夹 ID
     * @returns {Promise<number[]>} 所有子文件夹 ID 数组
     */
    const getAllSubFolderIds = async (conn, folderId) => {
        const ids = [];
        const stack = [folderId];

        while (stack.length > 0) {
            const currentId = stack.pop();
            ids.push(currentId);

            const [children] = await conn.query(
                'SELECT id FROM folders WHERE parent_id = ? AND status = 1',
                [currentId]
            );

            children.forEach(child => stack.push(child.id));
        }

        return ids;
    };

    try {
        const conn = await connection.getConnection();
        await conn.beginTransaction();

        try {
            const folderIds = await getAllSubFolderIds(conn, id);
            if (folderIds.length === 0) {
                await conn.rollback();
                ctx.status = 404;
                ctx.body = { code: 404, msg: '文件夹不存在' };
                return;
            }

            await conn.query('UPDATE folders SET status = 0 WHERE id IN (?)', [folderIds]);
            await conn.query('UPDATE files SET status = 0 WHERE folder_id IN (?)', [folderIds]);

            await conn.commit();
            await addUserLog(userId, `删除文件夹及其子内容 (ID: ${id})`, 12);

            ctx.body = { code: 200, msg: '文件夹删除成功（含子文件夹与文件）' };
        } catch (err) {
            await conn.rollback();
            throw err;
        } finally {
            conn.release();
        }
    } catch (error) {
        console.error('删除文件夹失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '删除失败，可能是文件夹不存在或已被删除' };
    }
};

/**
 * 重命名文件夹 & 更新路径
 * @route POST /api/folder/rename
 * @access 私有（需登录）
 */
exports.renameFolder = async (ctx) => {
    const { id, newName } = ctx.request.body;
    const userId = ctx.tokenObj?.id;

    if (!id || !newName || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '缺少必要参数' };
        return;
    }

    /**
     * 递归更新子文件夹路径
     * @param {object} conn - 数据库连接
     * @param {number} folderId - 当前文件夹 ID
     * @param {string} oldPrefix - 原始路径前缀
     * @param {string} newPrefix - 新路径前缀
     */
    const updateChildPathsRecursive = async (conn, folderId, oldPrefix, newPrefix) => {
        const [children] = await conn.query(
            'SELECT id, path FROM folders WHERE parent_id = ? AND status = 1',
            [folderId]
        );

        for (const child of children) {
            const newChildPath = child.path.replace(oldPrefix, newPrefix);
            await conn.query('UPDATE folders SET path = ? WHERE id = ?', [newChildPath, child.id]);
            await updateChildPathsRecursive(conn, child.id, oldPrefix, newPrefix);
        }
    };

    try {
        const conn = await connection.getConnection();
        await conn.beginTransaction();

        try {
            const [rows] = await conn.query(
                'SELECT name, parent_id, path FROM folders WHERE id = ? AND status = 1',
                [id]
            );
            if (rows.length === 0) {
                await conn.rollback();
                ctx.status = 404;
                ctx.body = { code: 404, msg: '文件夹不存在' };
                return;
            }

            const folder = rows[0];

            const [exist] = await conn.query(
                'SELECT id FROM folders WHERE parent_id = ? AND name = ? AND id != ? AND status = 1',
                [folder.parent_id, newName, id]
            );
            if (exist.length > 0) {
                await conn.rollback();
                ctx.status = 400;
                ctx.body = { code: 400, msg: '同级目录下已存在相同名称的文件夹' };
                return;
            }

            const newPath = folder.path.replace(/[^/]+$/, encodeURIComponent(newName));
            await conn.query('UPDATE folders SET name = ?, path = ? WHERE id = ?', [newName, newPath, id]);
            await updateChildPathsRecursive(conn, id, folder.path, newPath);

            await conn.commit();
            await addUserLog(userId, `重命名文件夹 "${folder.name}" -> "${newName}"`, 13);

            ctx.body = { code: 200, msg: '重命名成功' };
        } catch (err) {
            await conn.rollback();
            throw err;
        } finally {
            conn.release();
        }
    } catch (error) {
        console.error('重命名失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '重命名失败' };
    }
};

/**
 * 移动文件夹到另一个父目录
 * @route POST /api/folder/move
 * @access 私有（需登录）
 */
exports.moveFolder = async (ctx) => {
    const { id, newParentId } = ctx.request.body;
    const userId = ctx.tokenObj?.id;

    if (!id || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '缺少必要参数' };
        return;
    }

    /**
     * 判断是否为目标的后代（防止循环移动）
     * @param {object} conn - 数据库连接
     * @param {number} childId - 子文件夹 ID
     * @param {number|null} parentId - 目标父级 ID
     * @returns {Promise<boolean>}
     */
    const isDescendant = async (conn, childId, parentId) => {
        if (!parentId) return false;

        const [rows] = await conn.query(
            'SELECT parent_id FROM folders WHERE id = ? AND status = 1',
            [childId]
        );

        if (rows.length === 0) return false;

        const parent_id = rows[0].parent_id;
        if (parent_id == parentId) return true;

        return await isDescendant(conn, parent_id, parentId);
    };

    const updateChildPathsRecursive = async (conn, folderId, oldPrefix, newPrefix) => {
        const [children] = await conn.query(
            'SELECT id, path FROM folders WHERE parent_id = ? AND status = 1',
            [folderId]
        );

        for (const child of children) {
            const newChildPath = child.path.replace(oldPrefix, newPrefix);
            await conn.query('UPDATE folders SET path = ? WHERE id = ?', [newChildPath, child.id]);
            await updateChildPathsRecursive(conn, child.id, oldPrefix, newPrefix);
        }
    };

    try {
        const conn = await connection.getConnection();
        await conn.beginTransaction();

        try {
            const [rows] = await conn.query(
                'SELECT name, parent_id, path FROM folders WHERE id = ? AND status = 1',
                [id]
            );
            if (rows.length === 0) {
                await conn.rollback();
                ctx.status = 404;
                ctx.body = { code: 404, msg: '文件夹不存在' };
                return;
            }

            const folder = rows[0];

            let targetPath;
            if (newParentId === null) {
                targetPath = '/';
            } else {
                const [parent] = await conn.query(
                    'SELECT path FROM folders WHERE id = ? AND status = 1',
                    [newParentId]
                );
                if (parent.length === 0) {
                    await conn.rollback();
                    ctx.status = 400;
                    ctx.body = { code: 400, msg: '目标父文件夹不存在' };
                    return;
                }
                targetPath = parent[0].path + '/' + encodeURIComponent(folder.name);
            }

            if (await isDescendant(conn, id, newParentId)) {
                await conn.rollback();
                ctx.status = 400;
                ctx.body = { code: 400, msg: '禁止移动到自己的子文件夹中' };
                return;
            }

            const [exist] = await conn.query(
                'SELECT id FROM folders WHERE parent_id = ? AND name = ? AND id != ? AND status = 1',
                [newParentId, folder.name, id]
            );
            if (exist.length > 0) {
                await conn.rollback();
                ctx.status = 400;
                ctx.body = { code: 400, msg: '目标目录下已存在同名文件夹' };
                return;
            }

            await conn.query('UPDATE folders SET parent_id = ?, path = ? WHERE id = ?', [newParentId, targetPath, id]);
            await updateChildPathsRecursive(conn, id, folder.path, targetPath);

            await conn.commit();
            await addUserLog(userId, `移动文件夹 "${folder.name}" 到新目录`, 14);

            ctx.body = { code: 200, msg: '移动成功' };
        } catch (err) {
            await conn.rollback();
            throw err;
        } finally {
            conn.release();
        }
    } catch (error) {
        console.error('移动文件夹失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '移动失败' };
    }
};

// ========================================================================
//                            文件操作
// ========================================================================

/**
 * 确保上传目录存在（同步）
 * @param {string} dirPath - 目录路径
 */
const ensureUploadDir = (dirPath) => {
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
    }
};

/**
 * 生成唯一文件名
 * @param {string} originalName - 原始文件名
 * @param {number} userId - 用户 ID
 * @returns {string} 唯一文件名
 */
const generateUniqueFilename = (originalName, userId) => {
    const ext = path.extname(originalName);
    const baseName = path.basename(originalName, ext);
    const timestamp = Date.now();
    const random = Math.floor(Math.random() * 1000);
    return `${userId}_${baseName}_${timestamp}_${random}${ext}`;
};

/**
 * 构建存储路径和 URL
 * @param {object} file - 上传的文件对象
 * @param {number} userId - 用户 ID
 * @returns {{ filePath: string, fileUrl: string, storagePath: string }}
 */
const buildFilePathAndUrl = (file, userId) => {
    if (!file || !file.originalFilename) {
        throw new Error('文件对象缺少 originalFilename');
    }

    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');

    const dirRelative = `/files/${year}/${month}/${day}`;
    const dirAbsolute = path.join(__dirname, '../uploads', dirRelative);
    console.log(dirAbsolute);
    ensureUploadDir(dirAbsolute);

    // ✅ 使用 originalFilename
    const filename = generateUniqueFilename(file.originalFilename, userId);
    const filePath = path.join(dirAbsolute, filename);
    const fileUrl = `${dirRelative}/${filename}`;

    return { filePath, fileUrl };
};

/**
 * 上传文件
 * @route POST /api/file/upload
 * @access 私有（需登录）
 */
exports.uploadFile = async (ctx) => {
    const userId = ctx.tokenObj?.id;
    const { folderId } = ctx.request.body;
    const files = ctx.request.files?.file;

    if (!userId) {
        ctx.status = 401;
        ctx.body = { code: 401, msg: '用户未登录' };
        return;
    }

    if (!files) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '没有上传文件' };
        return;
    }

    const uploadedFiles = Array.isArray(files) ? files : [files];
    const results = [];

    try {
        for (const file of uploadedFiles) {
            // ✅ 使用 file.originalFilename
            if (!file || !file.originalFilename || file.size === 0) {
                console.warn('跳过无效文件:', file);
                continue;
            }

            const { filePath, fileUrl } = buildFilePathAndUrl(file, userId);
            try {
                await fsPromises.rename(file.filepath, filePath);
            } catch (error) {
                if (error.code === 'EXDEV') {
                    await fsPromises.copyFile(file.filepath, filePath);
                    await fsPromises.unlink(file.filepath); // 删除临时文件
                } else {
                    throw error;
                }
            }
            const [result] = await connection.query(
                `INSERT INTO files 
                 (upload_userid, name, folder_id, file_path, file_url, size, mime_type) 
                 VALUES (?, ?, ?, ?, ?, ?, ?)`,
                [
                    userId,
                    file.originalFilename,           // ✅ 使用 originalFilename
                    folderId || null,
                    filePath,
                    fileUrl,
                    file.size,
                    file.mimetype                   // 注意：是 mimetype，不是 type
                ]
            );

            const fileId = result.insertId;

            if (results.length === 0) {
                await addUserLog(userId, `上传文件 "${file.originalFilename}" (${file.size} 字节)`, 21);
            }

            results.push({
                id: fileId,
                name: file.originalFilename,
                url: fileUrl,
                size: file.size,
                mimeType: file.mimetype
            });
        }

        ctx.body = { code: 200, msg: '上传成功', data: results };
    } catch (error) {
        console.error('文件上传失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '文件处理失败' };
    }
};
/**
 * 多条件分页查询文件列表
 * @route GET /api/file/list
 * @access 私有（需登录）
 */
exports.getFileList = async (ctx) => {
    const userId = ctx.tokenObj?.id;
    if (!userId) {
        ctx.status = 401;
        ctx.body = { code: 401, msg: '用户未登录' };
        return;
    }

    const {
        folderId = null,
        keyword = '',
        page = 1,
        limit = 10,
        sortBy = 'created_at',
        sortOrder = 'DESC'
    } = ctx.query;

    const offset = (page - 1) * limit;

    const allowedSorts = ['created_at', 'name', 'size', 'updated_at'];
    const allowedOrders = ['ASC', 'DESC'];
    const safeSortBy = allowedSorts.includes(sortBy) ? sortBy : 'created_at';
    const safeSortOrder = allowedOrders.includes(sortOrder) ? sortOrder : 'DESC';

    try {
        let sql = `
            SELECT f.id, f.name, f.folder_id, f.file_path, f.file_url, 
                   f.size, f.mime_type, f.created_at, f.updated_at,
                   fo.name AS folder_name
            FROM files f
            LEFT JOIN folders fo ON f.folder_id = fo.id 
            WHERE f.upload_userid = ? AND f.status = 1
        `;
        const params = [userId];

        if (folderId !== null && folderId !== 'null') {
            sql += ' AND f.folder_id = ?';
            params.push(folderId);
        }

        if (keyword) {
            sql += ' AND f.name LIKE ?';
            params.push(`%${keyword}%`);
        }

        sql += ` ORDER BY f.${safeSortBy} ${safeSortOrder} LIMIT ? OFFSET ?`;
        params.push(parseInt(limit), parseInt(offset));

        const countSql = sql.replace(/SELECT[\s\S]+?FROM/, 'SELECT COUNT(*) as total FROM').split('ORDER')[0];

        const [rows] = await connection.query(sql, params);
        const [totalRes] = await connection.query(countSql, params.slice(0, -2));

        ctx.body = {
            code: 200,
            msg: '获取成功',
            data: {
                list: rows,
                pagination: {
                    total: totalRes[0].total,
                    page: parseInt(page),
                    limit: parseInt(limit),
                    pages: Math.ceil(totalRes[0].total / limit)
                }
            }
        };
    } catch (error) {
        console.error('查询文件列表失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '服务器错误' };
    }
};

/**
 * 删除文件（逻辑删除）
 * @route POST /api/file/delete
 * @access 私有（需登录）
 */
exports.deleteFile = async (ctx) => {
    const { ids } = ctx.request.body;
    const userId = ctx.tokenObj?.id;

    if (!ids || !Array.isArray(ids) || ids.length === 0 || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '缺少必要参数' };
        return;
    }

    try {
        const placeholders = ids.map(() => '?').join(',');
        const sql = `UPDATE files SET status = 0 WHERE id IN (${placeholders}) AND upload_userid = ?`;

        const [result] = await connection.query(sql, [...ids, userId]);

        if (result.affectedRows === 0) {
            ctx.body = { code: 404, msg: '未找到要删除的文件或无权限' };
            return;
        }

        await addUserLog(userId, `删除 ${result.affectedRows} 个文件`, 22);
        ctx.body = { code: 200, msg: `成功删除 ${result.affectedRows} 个文件` };
    } catch (error) {
        console.error('删除文件失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '删除失败' };
    }
};

/**
 * 重命名文件
 * @route POST /api/file/rename
 * @access 私有（需登录）
 */
exports.renameFile = async (ctx) => {
    const { id, newName } = ctx.request.body;
    const userId = ctx.tokenObj?.id;

    if (!id || !newName || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '缺少必要参数' };
        return;
    }

    try {
        const [rows] = await connection.query(
            `SELECT name, upload_userid FROM files WHERE id = ? AND status = 1`,
            [id]
        );

        if (rows.length === 0) {
            ctx.status = 404;
            ctx.body = { code: 404, msg: '文件不存在' };
            return;
        }
        
        const file = rows[0];
        if (file.upload_userid != userId) {
            ctx.status = 403;
            ctx.body = { code: 403, msg: '无权操作此文件' };
            return;
        }

        await connection.query('UPDATE files SET name = ? WHERE id = ?', [newName, id]);
        await addUserLog(userId, `重命名文件 "${file.name}" -> "${newName}"`, 23);
        ctx.body = { code: 200, msg: '重命名成功' };
    } catch (error) {
        console.error('重命名文件失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '服务器错误' };
    }
};

/**
 * 移动文件到其他文件夹
 * @route POST /api/file/move
 * @access 私有（需登录）
 */
exports.moveFile = async (ctx) => {
    const { ids, targetFolderId } = ctx.request.body;
    const userId = ctx.tokenObj?.id;

    if (!ids || !Array.isArray(ids) || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '缺少必要参数' };
        return;
    }

    if (targetFolderId) {
        const [folderRows] = await connection.query(
            'SELECT id FROM folders WHERE id = ? AND creationUser = ? AND status = 1',
            [targetFolderId, userId]
        );
        if (folderRows.length === 0) {
            ctx.status = 400;
            ctx.body = { code: 400, msg: '目标文件夹不存在或无权限' };
            return;
        }
    }

    try {
        const placeholders = ids.map(() => '?').join(',');
        const sql = `UPDATE files SET folder_id = ? WHERE id IN (${placeholders}) AND upload_userid = ?`;

        const [result] = await connection.query(sql, [targetFolderId || null, ...ids, userId]);

        await addUserLog(userId, `移动 ${result.affectedRows} 个文件到文件夹 ${targetFolderId || '根目录'}`, 24);
        ctx.body = { code: 200, msg: `移动成功，影响 ${result.affectedRows} 个文件` };
    } catch (error) {
        console.error('移动文件失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '移动失败' };
    }
};

/**
 * 安全下载文件（验证权限后流式输出）
 * @route GET /api/file/download
 * @access 私有（需登录）
 */
exports.downloadFile = async (ctx) => {
    const fileId = ctx.query.id;
    const userId = ctx.tokenObj?.id;

    if (!fileId || !userId) {
        ctx.status = 400;
        ctx.body = { code: 400, msg: '参数缺失' };
        return;
    }

    try {
        const [rows] = await connection.query(
            `SELECT f.name, f.file_path, f.upload_userid 
             FROM files f 
             WHERE f.id = ? AND f.status = 1`,
            [fileId]
        );

        if (rows.length === 0) {
            ctx.status = 404;
            ctx.body = { code: 404, msg: '文件不存在' };
            return;
        }

        const file = rows[0];
        if (file.upload_userid !== userId) {
            ctx.status = 403;
            ctx.body = { code: 403, msg: '无权下载此文件' };
            return;
        }

        if (!await fsPromises.stat(file.file_path).catch(() => null)) {
            ctx.status = 404;
            ctx.body = { code: 404, msg: '文件物理路径不存在' };
            return;
        }

        ctx.set('Content-Disposition', `attachment; filename="${encodeURIComponent(file.name)}"`);
        ctx.set('Content-Type', 'application/octet-stream');
        ctx.body = fs.createReadStream(file.file_path);
    } catch (error) {
        console.error('下载失败:', error);
        ctx.status = 500;
        ctx.body = { code: 500, msg: '下载失败' };
    }
};